library(scPipe)
library(scater)
Attaching package: ‘scater’
The following object is masked from ‘package:S4Vectors’:
rename
The following objects are masked from ‘package:dplyr’:
arrange, filter, mutate, rename
The following object is masked from ‘package:stats’:
filter
library(scran)
library(ggplot2)
library(Seurat)
library(cowplot)
library(sctransform)
library(dplyr)
library(readr)
library(ggrepel)
gene_filter = function(sce){
keep1 = (apply(counts(sce), 1, function(x) mean(x[x>0])) > 1.1) # average count larger than 1.1
keep2 = (rowSums(counts(sce)>0) > 5) # expressed in more than 5 cells
sce = sce[(keep1 & keep2), ]
return(sce)
}
scran_norm = function(sce){
sce = computeSumFactors(sce)
sce = normalize(sce)
return(sce)
}
scran_high_var = function(sce,topn=3000){
var.fit <- trendVar(sce, method="loess", use.spikes=FALSE)
var.out <- decomposeVar(sce, var.fit)
hvg.out <- var.out[order(var.out$bio, decreasing=TRUE)[1:topn], ]
rowData(sce)$hi_var = FALSE
rowData(sce)$hi_var[rownames(rowData(sce)) %in% rownames(hvg.out)] = TRUE
return(sce)
}
sce_LC187 = create_sce_by_dir(datadir="/Users/tian.l/Dropbox/research/Dach1_paper/NN126/LC187",organism="mmusculus_gene_ensembl",gene_id_type="ensembl_gene_id")
sce_LC188 = create_sce_by_dir(datadir="/Users/tian.l/Dropbox/research/Dach1_paper/NN126/LC188",organism="mmusculus_gene_ensembl",gene_id_type="ensembl_gene_id")
sce_LC189 = create_sce_by_dir(datadir="/Users/tian.l/Dropbox/research/Dach1_paper/NN126/LC189",organism="mmusculus_gene_ensembl",gene_id_type="ensembl_gene_id")
sce_LC190 = create_sce_by_dir(datadir="/Users/tian.l/Dropbox/research/Dach1_paper/NN126/LC190",organism="mmusculus_gene_ensembl",gene_id_type="ensembl_gene_id")
sce_LC187 = calculate_QC_metrics(sce_LC187)
cannot connect to the ensembl database. ERROR:
Error in useMart("ensembl"): could not find function "useMart"
Try to use org.Mm.eg.db.
'select()' returned 1:many mapping between keys and columns
cannot connect to the ensembl database. ERROR:
Error in useMart("ensembl"): could not find function "useMart"
Try to use org.Mm.eg.db.
'select()' returned 1:many mapping between keys and columns
sce_LC188 = calculate_QC_metrics(sce_LC188)
cannot connect to the ensembl database. ERROR:
Error in useMart("ensembl"): could not find function "useMart"
Try to use org.Mm.eg.db.
'select()' returned 1:many mapping between keys and columns
cannot connect to the ensembl database. ERROR:
Error in useMart("ensembl"): could not find function "useMart"
Try to use org.Mm.eg.db.
'select()' returned 1:many mapping between keys and columns
sce_LC189 = calculate_QC_metrics(sce_LC189)
cannot connect to the ensembl database. ERROR:
Error in useMart("ensembl"): could not find function "useMart"
Try to use org.Mm.eg.db.
'select()' returned 1:many mapping between keys and columns
cannot connect to the ensembl database. ERROR:
Error in useMart("ensembl"): could not find function "useMart"
Try to use org.Mm.eg.db.
'select()' returned 1:many mapping between keys and columns
sce_LC190 = calculate_QC_metrics(sce_LC190)
cannot connect to the ensembl database. ERROR:
Error in useMart("ensembl"): could not find function "useMart"
Try to use org.Mm.eg.db.
'select()' returned 1:many mapping between keys and columns
cannot connect to the ensembl database. ERROR:
Error in useMart("ensembl"): could not find function "useMart"
Try to use org.Mm.eg.db.
'select()' returned 1:many mapping between keys and columns
sce_LC187_qc = detect_outlier(sce_LC187,comp = 2)
sce_LC188_qc = detect_outlier(sce_LC188,comp = 2)
sce_LC189_qc = detect_outlier(sce_LC189,comp = 2)
sce_LC190_qc = detect_outlier(sce_LC190,comp = 2)
sce_LC187_qc = remove_outliers(sce_LC187_qc)
sce_LC188_qc = remove_outliers(sce_LC188_qc)
sce_LC189_qc = remove_outliers(sce_LC189_qc)
sce_LC190_qc = remove_outliers(sce_LC190_qc)
sce_LC187_qc = gene_filter(sce_LC187_qc)
sce_LC188_qc = gene_filter(sce_LC188_qc)
sce_LC189_qc = gene_filter(sce_LC189_qc)
sce_LC190_qc = gene_filter(sce_LC190_qc)
sce_LC187_qc = convert_geneid(sce_LC187_qc)
cannot connect to the ensembl database. ERROR:
Error in useMart("ensembl"): could not find function "useMart"
Try to use org.Mm.eg.db.
'select()' returned 1:many mapping between keys and columns
Number of NA in new gene id: 2742. Duplicated id: 5.5
First 5 duplicated:
c(NA, NA, NA, NA, NA, NA)c("ENSMUSG00000022684", "ENSMUSG00000090093", "ENSMUSG00000109865", "ENSMUSG00000079737", "ENSMUSG00000024571", "ENSMUSG00000071497")c("Bfar", "Gm14391", "Hspa14", "Bfar", "Txnl4a", "Nutf2")
sce_LC188_qc = convert_geneid(sce_LC188_qc)
cannot connect to the ensembl database. ERROR:
Error in useMart("ensembl"): could not find function "useMart"
Try to use org.Mm.eg.db.
'select()' returned 1:many mapping between keys and columns
Number of NA in new gene id: 2622. Duplicated id: 5.5
First 5 duplicated:
c(NA, NA, NA, NA, NA, NA)c("ENSMUSG00000090093", "ENSMUSG00000051396", "ENSMUSG00000109865", "ENSMUSG00000022684", "ENSMUSG00000068240", "ENSMUSG00000008450")c("Gm14391", "Hspa14", "Hspa14", "Bfar", "Uba52", "Nutf2")
sce_LC189_qc = convert_geneid(sce_LC189_qc)
cannot connect to the ensembl database. ERROR:
Error in useMart("ensembl"): could not find function "useMart"
Try to use org.Mm.eg.db.
'select()' returned 1:many mapping between keys and columns
Number of NA in new gene id: 2685. Duplicated id: 4.5
First 5 duplicated:
c(NA, NA, NA, NA, NA, NA)c("ENSMUSG00000109865", "ENSMUSG00000090093", "ENSMUSG00000057130", "ENSMUSG00000071497", "ENSMUSG00000068240", "ENSMUSG00000024571")c("Hspa14", "Gm14391", "Txnl4a", "Nutf2", "Uba52", "Txnl4a")
sce_LC190_qc = convert_geneid(sce_LC190_qc)
cannot connect to the ensembl database. ERROR:
Error in useMart("ensembl"): could not find function "useMart"
Try to use org.Mm.eg.db.
'select()' returned 1:many mapping between keys and columns
Number of NA in new gene id: 2897. Duplicated id: 5.5
First 5 duplicated:
c(NA, NA, NA, NA, NA, NA)c("ENSMUSG00000068240", "ENSMUSG00000090093", "ENSMUSG00000057130", "ENSMUSG00000022684", "ENSMUSG00000051396", "ENSMUSG00000109865")c("Uba52", "Gm14391", "Txnl4a", "Bfar", "Hspa14", "Hspa14")
comm_genes = Reduce(intersect,list(rownames(sce_LC187_qc),
rownames(sce_LC188_qc),
rownames(sce_LC189_qc),
rownames(sce_LC190_qc)))
comm_genes = comm_genes[!grepl("ENSMUSG",comm_genes)]
comm_genes = comm_genes[!grepl("ERCC",comm_genes)]
comm_genes = comm_genes[!grepl("Rik",comm_genes)]
comm_genes = comm_genes[!grepl("Hist",comm_genes)]
comm_genes = comm_genes[!grepl("^Rpl",comm_genes)]
comm_genes = comm_genes[!grepl("^Rps",comm_genes)]
sce_LC187_qc = sce_LC187_qc[comm_genes,]
sce_LC188_qc = sce_LC188_qc[comm_genes,]
sce_LC189_qc = sce_LC189_qc[comm_genes,]
sce_LC190_qc = sce_LC190_qc[comm_genes,]
colnames(sce_LC187_qc) = paste("LC187",colnames(sce_LC187_qc),sep="_")
colnames(sce_LC188_qc) = paste("LC188",colnames(sce_LC188_qc),sep="_")
colnames(sce_LC189_qc) = paste("LC189",colnames(sce_LC189_qc),sep="_")
colnames(sce_LC190_qc) = paste("LC190",colnames(sce_LC190_qc),sep="_")
sce_LC187_qc$batch = "LC187"
sce_LC188_qc$batch = "LC188"
sce_LC189_qc$batch = "LC189"
sce_LC190_qc$batch = "LC190"
#FACS_anno <- read.csv("~/Dropbox/research/Dach1_paper/NN126/Dach1_GFPLib_NN126_SeqPrimer layout_Sara_TomeiJan19.csv", stringsAsFactors=FALSE)
FACS_anno <- read.csv("~/Dropbox/research/Dach1_paper/NN126/Dach1_GFP_transformed_values.csv", stringsAsFactors=FALSE)
FACS_anno = FACS_anno[!is.na(FACS_anno$Dach1GFP),]
FACS_anno$cell_id = paste(FACS_anno$Plate,paste0(FACS_anno$Row,FACS_anno$Column),sep="_")
rownames(FACS_anno) = FACS_anno$cell_id
FACS_anno = FACS_anno[,c("Dach1GFP","CD11b","CD150","CD127",
"cKit","CD135","Sca1","PI")]
# FACS_anno = apply(FACS_anno,2,function(x){log10(x-min(min(x),0)+1)})
# FACS_anno = as.data.frame(FACS_anno)
# FACS_anno$CD135[FACS_anno$CD135<2] = 2
# FACS_anno$CD150[FACS_anno$CD150<1.4] = 1.4
# FACS_anno$CD11b[FACS_anno$CD11b<1.8] = 1.8
# FACS_anno$CD127[FACS_anno$CD127<1.5] = 1.5
# FACS_anno$Dach1GFP[FACS_anno$Dach1GFP<1.9] = 1.9
pdf("marker_scaled_plot.pdf")
ggplot(data=FACS_anno,aes(x=Dach1GFP,y=CD135,col=CD127))+
geom_point(alpha=0.5)+
scale_color_gradientn(colours = rev(rainbow(5)))+
theme_bw()
ggplot(data=FACS_anno,aes(x=CD135,y=CD150,col=CD127))+
geom_point(alpha=0.5)+
scale_color_gradientn(colours = rev(rainbow(5)))+
theme_bw()
ggplot(data=FACS_anno,aes(x=CD127,y=cKit))+
geom_point(alpha=0.5)+
theme_bw()
ggplot(data=FACS_anno,aes(x=CD135,y=CD127))+
geom_point(alpha=0.5)+
theme_bw()
dev.off()
null device
1
colData(sce_LC187_qc) = cbind(colData(sce_LC187_qc),DataFrame(FACS_anno[colnames(sce_LC187_qc),]))
colData(sce_LC188_qc) = cbind(colData(sce_LC188_qc),DataFrame(FACS_anno[colnames(sce_LC188_qc),]))
colData(sce_LC189_qc) = cbind(colData(sce_LC189_qc),DataFrame(FACS_anno[colnames(sce_LC189_qc),]))
colData(sce_LC190_qc) = cbind(colData(sce_LC190_qc),DataFrame(FACS_anno[colnames(sce_LC190_qc),]))
srt_187 <- CreateSeuratObject(counts = counts(sce_LC187_qc),meta.data=as.data.frame(colData(sce_LC187_qc)))
srt_187 <- SCTransform(object = srt_187, variable.features.n=2000, verbose = FALSE)
srt_188 <- CreateSeuratObject(counts = counts(sce_LC188_qc),meta.data=as.data.frame(colData(sce_LC188_qc)))
srt_188 <- SCTransform(object = srt_188, variable.features.n=2000,verbose = FALSE)
srt_189 <- CreateSeuratObject(counts = counts(sce_LC189_qc),meta.data=as.data.frame(colData(sce_LC189_qc)))
srt_189 <- SCTransform(object = srt_189, variable.features.n=2000,verbose = FALSE)
srt_190 <- CreateSeuratObject(counts = counts(sce_LC190_qc),meta.data=as.data.frame(colData(sce_LC190_qc)))
srt_190 <- SCTransform(object = srt_190, variable.features.n=2000,verbose = FALSE)
options(future.globals.maxSize = 2100 * 1024^2)
immune.features <- SelectIntegrationFeatures(object.list = list(srt_187, srt_188, srt_189, srt_190), nfeatures = 2000, verbose = FALSE)
immune.combined <- PrepSCTIntegration(object.list = list(srt_187, srt_188, srt_189, srt_190), anchor.features = immune.features, verbose = FALSE)
immune.anchors <- FindIntegrationAnchors(object.list = immune.combined, normalization.method = "SCT",
anchor.features = immune.features, verbose = FALSE)
srt_combine <- IntegrateData(anchorset = immune.anchors, normalization.method = "SCT", dims = 1:20,
verbose = FALSE)
srt_combine = FindIntegrationAnchors(object.list = list(srt_187, srt_188, srt_189, srt_190), dims = 1:15, verbose = FALSE)
srt_combine <- IntegrateData(anchorset = srt_combine, dims = 1:15, verbose = FALSE)
DefaultAssay(object = srt_combine) <- "integrated"
srt_combine <- ScaleData(object = srt_combine, verbose = FALSE)
srt_combine <- RunPCA(object = srt_combine, verbose = FALSE)
srt_combine <- RunUMAP(object = srt_combine, dims = 1:15, verbose = FALSE)
srt_combine <- FindNeighbors(object = srt_combine, dims = 1:15, verbose = FALSE)
srt_combine <- FindClusters(object = srt_combine, verbose = FALSE)
# Visualization
p1 <- DimPlot(srt_combine, reduction = "umap", group.by = "batch")
p2 <- DimPlot(srt_combine, reduction = "umap", label = TRUE)
plot_grid(p1, p2)

srt_combine = srt_combine[,!is.na(srt_combine$Dach1GFP)]
FeaturePlot(object = srt_combine, features =c("Dach1GFP","CD11b","CD150","CD127","cKit","CD135","Sca1","PI"), pt.size = 0.5)

VlnPlot(object = srt_combine, features= c("Gata2","Dntt","Ctsg","Dach1","Gata1"),ncol=2,pt.size=0.1)

srt_combine.markers <- FindAllMarkers(srt_combine, only.pos = TRUE,verbose=FALSE)
top10 <- srt_combine.markers %>% group_by(cluster) %>% top_n(n = 10, wt = avg_logFC)
DoHeatmap(srt_combine, features = top10$gene)

VlnPlot(object = srt_combine[,!is.na(srt_combine$CD135)], features= c("Dach1GFP","CD150","CD135","CD127","cKit"),ncol=2,pt.size=0.1)

CD127_threshold = 900
ggplot(data=NULL,aes(x=srt_combine@meta.data$CD135,y=srt_combine@meta.data$CD127,col=srt_combine@active.ident))+
geom_hline(yintercept = CD127_threshold,linetype="dotted")+
geom_point(alpha=0.5,size=2)+
labs(x="CD135",y="CD127")+
theme_bw()

ggplot(data=NULL,aes(x=srt_combine@meta.data$cKit,y=srt_combine@meta.data$CD127,col=srt_combine@active.ident))+
geom_hline(yintercept = CD127_threshold,linetype="dotted")+
geom_point(alpha=0.5,size=2)+
labs(x="cKit",y="CD127")+
theme_bw()

filter cells
- remove CD127 high
- remove cluster 7 and 8
#srt_combine_filter <- SubsetData(object = srt_combine,ident.remove = c(7,8))
srt_combine_filter <- SubsetData(object = srt_combine_filter, subset.name="CD127",high.threshold=CD127_threshold)
'SubsetData' is deprecated.
Use 'subset' instead.
See help("Deprecated")'OldWhichCells' is deprecated.
Use 'WhichCells' instead.
See help("Deprecated")
srt_combine_filter <- RunPCA(object = srt_combine_filter, verbose = FALSE)
srt_combine_filter <- RunUMAP(object = srt_combine_filter, dims = 1:15, verbose = FALSE)
srt_combine_filter <- FindNeighbors(object = srt_combine_filter, dims = 1:15, verbose = FALSE)
srt_combine_filter <- FindClusters(object = srt_combine_filter, verbose = FALSE)
# Visualization
p1 <- DimPlot(srt_combine_filter, reduction = "umap", group.by = "batch")
p2 <- DimPlot(srt_combine_filter, reduction = "umap", label = TRUE)
plot_grid(p1, p2)

heatmap after filtering
srt_combine_filter.markers <- FindAllMarkers(srt_combine_filter, logfc.threshold = 0.1,only.pos = TRUE,verbose=FALSE)
top10 <- srt_combine_filter.markers %>% group_by(cluster) %>% top_n(n = 10, wt = avg_logFC)
DoHeatmap(srt_combine_filter, features = top10$gene)

VlnPlot(object = srt_combine_filter, features= c("Dach1GFP","CD150","CD135","CD127","cKit"),ncol=2,pt.size=0.1)

FeaturePlot(object = srt_combine_filter, features =c("Dach1GFP","CD11b","CD150","CD127","cKit","CD135","Sca1","PI"), pt.size = 0.5)

p1=ggplot(data=NULL,aes(x=srt_combine_filter@meta.data$CD135,y=srt_combine_filter@meta.data$CD150,col=srt_combine_filter@active.ident))+
geom_point(alpha=0.5,size=2)+
labs(x="CD135",y="CD150",col="cluster")+
theme_bw()
p2=ggplot(data=NULL,aes(x=srt_combine_filter@meta.data$Dach1GFP,y=srt_combine_filter@meta.data$CD135,col=srt_combine_filter@active.ident))+
geom_point(alpha=0.5,size=2)+
labs(x="Dach1GFP",y="CD135",col="cluster")+
theme_bw()
p3=ggplot(data=NULL,aes(x=srt_combine_filter@meta.data$cKit,y=srt_combine_filter@meta.data$Sca1,col=srt_combine_filter@active.ident))+
geom_point(alpha=0.5,size=2)+
labs(x="cKit",y="Sca1",col="cluster")+
theme_bw()
p4=ggplot(data=NULL,aes(x=srt_combine_filter@meta.data$CD135,y=srt_combine_filter@meta.data$CD127,col=srt_combine_filter@active.ident))+
geom_point(alpha=0.5,size=2)+
labs(x="CD135",y="CD127",col="cluster")+
theme_bw()
plot_grid(p1, p2, p3, p4,ncol=2)

find potential surface protein
mouse_surface_protein <- read_csv("~/Dropbox/research/sis_seq/mouse_surface_protein.csv")
Parsed with column specification:
cols(
organism = col_character(),
ID_link = col_character(),
`CSPA category` = col_character(),
UP_Protein_name = col_character(),
UP_entry_name = col_character(),
CD = col_character(),
ENTREZ_gene_ID = col_double(),
`ENTREZ gene symbol` = col_character(),
`protein probability` = col_double(),
`count detection in different cell types` = col_double(),
`Phobius TM predicted yes/no` = col_double(),
`# predicted TM domains` = col_double(),
GPI = col_double(),
`UniProt cell surface` = col_character()
)
surface_pro_name = mouse_surface_protein$`ENTREZ gene symbol`
surface_pro_name = surface_pro_name[surface_pro_name %in% rownames(srt_combine_filter)]
length(surface_pro_name)
[1] 151
cluster.surface_markers = srt_combine_filter.markers
cluster.surface_markers$is_surface_pro=FALSE
cluster.surface_markers$is_surface_pro[rownames(cluster.surface_markers) %in% surface_pro_name] = TRUE
cluster.surface_markers = cluster.surface_markers[cluster.surface_markers$is_surface_pro,]
cluster.surface_markers
heatmap of all surface proteins
DoHeatmap(srt_combine_filter, features = rownames(cluster.surface_markers))

surface protins that ralated to cluster 2
cluster.surface_markers_2 = cluster.surface_markers[cluster.surface_markers$cluster==2,]
cluster.surface_markers_2
imm_data = read.csv("/Users/tian.l/Dropbox/research/sis_seq/immgen/V1_ImmGenn-Official-Oct-2012.csv", skip=2, check.names=FALSE, as.is=TRUE)
cell.fac = gsub("#....", "", colnames(imm_data))
cell.fac = gsub("#...", "", cell.fac)
cell.fac = gsub("#.", "", cell.fac)
cell.fac = gsub("\\.", "_", cell.fac)
cell.fac = gsub("\\-", "neg", cell.fac)
cell.fac = gsub("\\+", "pos", cell.fac)
cell.fac = gsub("\\/", "div", cell.fac)
colnames(imm_data) = cell.fac
imm_data_surface = imm_data[imm_data$`Gene Symbol` %in% cluster.surface_markers_2$gene,]
imm_data_surface = imm_data_surface[!duplicated(imm_data_surface$`Gene Symbol`),]
rownames(imm_data_surface) = imm_data_surface$`Gene Symbol`
imm_data_surface=imm_data_surface[,-c(1,2)]
imm_data_surface = as.matrix(imm_data_surface)
pdf("cluster2_surface_protein.immgen.pdf",width=15,height = 80)
pheatmap::pheatmap(t(log2(imm_data_surface+1)),scale = "column",fontsize_col=3,fontsize_row=10)
dev.off()
null device
1
clu2.markers = FindMarkers(srt_combine_filter,ident.1=2,verbose = FALSE)
clu2.markers
clu2.markers$gene_name = ""
clu2.markers$gene_name[clu2.markers$p_val_adj<0.0001 & clu2.markers$avg_logFC<0]= rownames(clu2.markers)[clu2.markers$p_val_adj<0.0001 & clu2.markers$avg_logFC<0]
clu2.markers$gene_name[clu2.markers$p_val_adj<1e-7 & clu2.markers$avg_logFC>0]= rownames(clu2.markers)[clu2.markers$p_val_adj<1e-7 & clu2.markers$avg_logFC>0]
ggplot(data=clu2.markers,aes(x=-log10(p_val_adj),y=avg_logFC,label=gene_name))+
geom_point()+
geom_vline(xintercept = 2,linetype="dotted")+
geom_text_repel()+
theme_bw()

save.image("~/Dropbox/research/Dach1_paper/NN126/NN126_Seurat_analysis.RData")
LS0tCnRpdGxlOiAicHJvY2VzcyBOTjEyNiBTZXVyYXR2MyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CmxpYnJhcnkoc2NQaXBlKQpsaWJyYXJ5KHNjYXRlcikKbGlicmFyeShzY3JhbikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KHNjdHJhbnNmb3JtKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGdncmVwZWwpCmBgYAoKCgoKYGBge3J9CmdlbmVfZmlsdGVyID0gZnVuY3Rpb24oc2NlKXsKICBrZWVwMSA9IChhcHBseShjb3VudHMoc2NlKSwgMSwgZnVuY3Rpb24oeCkgbWVhbih4W3g+MF0pKSA+IDEuMSkgICMgYXZlcmFnZSBjb3VudCBsYXJnZXIgdGhhbiAxLjEKICBrZWVwMiA9IChyb3dTdW1zKGNvdW50cyhzY2UpPjApID4gNSkgIyBleHByZXNzZWQgaW4gbW9yZSB0aGFuIDUgY2VsbHMKICBzY2UgPSBzY2VbKGtlZXAxICYga2VlcDIpLCBdCiAgcmV0dXJuKHNjZSkKfQpzY3Jhbl9ub3JtID0gZnVuY3Rpb24oc2NlKXsKICBzY2UgPSBjb21wdXRlU3VtRmFjdG9ycyhzY2UpCiAgc2NlID0gbm9ybWFsaXplKHNjZSkKICByZXR1cm4oc2NlKQp9CnNjcmFuX2hpZ2hfdmFyID0gZnVuY3Rpb24oc2NlLHRvcG49MzAwMCl7CiAgdmFyLmZpdCA8LSB0cmVuZFZhcihzY2UsIG1ldGhvZD0ibG9lc3MiLCB1c2Uuc3Bpa2VzPUZBTFNFKQogIHZhci5vdXQgPC0gZGVjb21wb3NlVmFyKHNjZSwgdmFyLmZpdCkKICBodmcub3V0IDwtIHZhci5vdXRbb3JkZXIodmFyLm91dCRiaW8sIGRlY3JlYXNpbmc9VFJVRSlbMTp0b3BuXSwgXQogIHJvd0RhdGEoc2NlKSRoaV92YXIgPSBGQUxTRQogIHJvd0RhdGEoc2NlKSRoaV92YXJbcm93bmFtZXMocm93RGF0YShzY2UpKSAlaW4lIHJvd25hbWVzKGh2Zy5vdXQpXSA9IFRSVUUKICByZXR1cm4oc2NlKQp9CmBgYAoKCmBgYHtyfQpzY2VfTEMxODcgPSBjcmVhdGVfc2NlX2J5X2RpcihkYXRhZGlyPSIvVXNlcnMvdGlhbi5sL0Ryb3Bib3gvcmVzZWFyY2gvRGFjaDFfcGFwZXIvTk4xMjYvTEMxODciLG9yZ2FuaXNtPSJtbXVzY3VsdXNfZ2VuZV9lbnNlbWJsIixnZW5lX2lkX3R5cGU9ImVuc2VtYmxfZ2VuZV9pZCIpCnNjZV9MQzE4OCA9IGNyZWF0ZV9zY2VfYnlfZGlyKGRhdGFkaXI9Ii9Vc2Vycy90aWFuLmwvRHJvcGJveC9yZXNlYXJjaC9EYWNoMV9wYXBlci9OTjEyNi9MQzE4OCIsb3JnYW5pc209Im1tdXNjdWx1c19nZW5lX2Vuc2VtYmwiLGdlbmVfaWRfdHlwZT0iZW5zZW1ibF9nZW5lX2lkIikKc2NlX0xDMTg5ID0gY3JlYXRlX3NjZV9ieV9kaXIoZGF0YWRpcj0iL1VzZXJzL3RpYW4ubC9Ecm9wYm94L3Jlc2VhcmNoL0RhY2gxX3BhcGVyL05OMTI2L0xDMTg5IixvcmdhbmlzbT0ibW11c2N1bHVzX2dlbmVfZW5zZW1ibCIsZ2VuZV9pZF90eXBlPSJlbnNlbWJsX2dlbmVfaWQiKQpzY2VfTEMxOTAgPSBjcmVhdGVfc2NlX2J5X2RpcihkYXRhZGlyPSIvVXNlcnMvdGlhbi5sL0Ryb3Bib3gvcmVzZWFyY2gvRGFjaDFfcGFwZXIvTk4xMjYvTEMxOTAiLG9yZ2FuaXNtPSJtbXVzY3VsdXNfZ2VuZV9lbnNlbWJsIixnZW5lX2lkX3R5cGU9ImVuc2VtYmxfZ2VuZV9pZCIpCmBgYAoKCgpgYGB7cn0Kc2NlX0xDMTg3ID0gY2FsY3VsYXRlX1FDX21ldHJpY3Moc2NlX0xDMTg3KQpzY2VfTEMxODggPSBjYWxjdWxhdGVfUUNfbWV0cmljcyhzY2VfTEMxODgpCnNjZV9MQzE4OSA9IGNhbGN1bGF0ZV9RQ19tZXRyaWNzKHNjZV9MQzE4OSkKc2NlX0xDMTkwID0gY2FsY3VsYXRlX1FDX21ldHJpY3Moc2NlX0xDMTkwKQpgYGAKCgoKYGBge3J9CnNjZV9MQzE4N19xYyA9IGRldGVjdF9vdXRsaWVyKHNjZV9MQzE4Nyxjb21wID0gMikKc2NlX0xDMTg4X3FjID0gZGV0ZWN0X291dGxpZXIoc2NlX0xDMTg4LGNvbXAgPSAyKQpzY2VfTEMxODlfcWMgPSBkZXRlY3Rfb3V0bGllcihzY2VfTEMxODksY29tcCA9IDIpCnNjZV9MQzE5MF9xYyA9IGRldGVjdF9vdXRsaWVyKHNjZV9MQzE5MCxjb21wID0gMikKCnNjZV9MQzE4N19xYyA9IHJlbW92ZV9vdXRsaWVycyhzY2VfTEMxODdfcWMpCnNjZV9MQzE4OF9xYyA9IHJlbW92ZV9vdXRsaWVycyhzY2VfTEMxODhfcWMpCnNjZV9MQzE4OV9xYyA9IHJlbW92ZV9vdXRsaWVycyhzY2VfTEMxODlfcWMpCnNjZV9MQzE5MF9xYyA9IHJlbW92ZV9vdXRsaWVycyhzY2VfTEMxOTBfcWMpCgpzY2VfTEMxODdfcWMgPSBnZW5lX2ZpbHRlcihzY2VfTEMxODdfcWMpCnNjZV9MQzE4OF9xYyA9IGdlbmVfZmlsdGVyKHNjZV9MQzE4OF9xYykKc2NlX0xDMTg5X3FjID0gZ2VuZV9maWx0ZXIoc2NlX0xDMTg5X3FjKQpzY2VfTEMxOTBfcWMgPSBnZW5lX2ZpbHRlcihzY2VfTEMxOTBfcWMpCgoKCnNjZV9MQzE4N19xYyA9IGNvbnZlcnRfZ2VuZWlkKHNjZV9MQzE4N19xYykKc2NlX0xDMTg4X3FjID0gY29udmVydF9nZW5laWQoc2NlX0xDMTg4X3FjKQpzY2VfTEMxODlfcWMgPSBjb252ZXJ0X2dlbmVpZChzY2VfTEMxODlfcWMpCnNjZV9MQzE5MF9xYyA9IGNvbnZlcnRfZ2VuZWlkKHNjZV9MQzE5MF9xYykKCmNvbW1fZ2VuZXMgPSBSZWR1Y2UoaW50ZXJzZWN0LGxpc3Qocm93bmFtZXMoc2NlX0xDMTg3X3FjKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3duYW1lcyhzY2VfTEMxODhfcWMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzKHNjZV9MQzE4OV9xYyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93bmFtZXMoc2NlX0xDMTkwX3FjKSkpCgpjb21tX2dlbmVzID0gY29tbV9nZW5lc1shZ3JlcGwoIkVOU01VU0ciLGNvbW1fZ2VuZXMpXQpjb21tX2dlbmVzID0gY29tbV9nZW5lc1shZ3JlcGwoIkVSQ0MiLGNvbW1fZ2VuZXMpXQpjb21tX2dlbmVzID0gY29tbV9nZW5lc1shZ3JlcGwoIlJpayIsY29tbV9nZW5lcyldCmNvbW1fZ2VuZXMgPSBjb21tX2dlbmVzWyFncmVwbCgiSGlzdCIsY29tbV9nZW5lcyldCmNvbW1fZ2VuZXMgPSBjb21tX2dlbmVzWyFncmVwbCgiXlJwbCIsY29tbV9nZW5lcyldCmNvbW1fZ2VuZXMgPSBjb21tX2dlbmVzWyFncmVwbCgiXlJwcyIsY29tbV9nZW5lcyldCgpzY2VfTEMxODdfcWMgPSBzY2VfTEMxODdfcWNbY29tbV9nZW5lcyxdCnNjZV9MQzE4OF9xYyA9IHNjZV9MQzE4OF9xY1tjb21tX2dlbmVzLF0Kc2NlX0xDMTg5X3FjID0gc2NlX0xDMTg5X3FjW2NvbW1fZ2VuZXMsXQpzY2VfTEMxOTBfcWMgPSBzY2VfTEMxOTBfcWNbY29tbV9nZW5lcyxdCgpgYGAKYGBge3J9CmNvbG5hbWVzKHNjZV9MQzE4N19xYykgPSBwYXN0ZSgiTEMxODciLGNvbG5hbWVzKHNjZV9MQzE4N19xYyksc2VwPSJfIikKY29sbmFtZXMoc2NlX0xDMTg4X3FjKSA9IHBhc3RlKCJMQzE4OCIsY29sbmFtZXMoc2NlX0xDMTg4X3FjKSxzZXA9Il8iKQpjb2xuYW1lcyhzY2VfTEMxODlfcWMpID0gcGFzdGUoIkxDMTg5Iixjb2xuYW1lcyhzY2VfTEMxODlfcWMpLHNlcD0iXyIpCmNvbG5hbWVzKHNjZV9MQzE5MF9xYykgPSBwYXN0ZSgiTEMxOTAiLGNvbG5hbWVzKHNjZV9MQzE5MF9xYyksc2VwPSJfIikKc2NlX0xDMTg3X3FjJGJhdGNoID0gIkxDMTg3IgpzY2VfTEMxODhfcWMkYmF0Y2ggPSAiTEMxODgiCnNjZV9MQzE4OV9xYyRiYXRjaCA9ICJMQzE4OSIKc2NlX0xDMTkwX3FjJGJhdGNoID0gIkxDMTkwIgpgYGAKCmBgYHtyfQojRkFDU19hbm5vIDwtIHJlYWQuY3N2KCJ+L0Ryb3Bib3gvcmVzZWFyY2gvRGFjaDFfcGFwZXIvTk4xMjYvRGFjaDFfR0ZQTGliX05OMTI2X1NlcVByaW1lciBsYXlvdXRfU2FyYV9Ub21laUphbjE5LmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UpCkZBQ1NfYW5ubyA8LSByZWFkLmNzdigifi9Ecm9wYm94L3Jlc2VhcmNoL0RhY2gxX3BhcGVyL05OMTI2L0RhY2gxX0dGUF90cmFuc2Zvcm1lZF92YWx1ZXMuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkKCkZBQ1NfYW5ubyA9IEZBQ1NfYW5ub1shaXMubmEoRkFDU19hbm5vJERhY2gxR0ZQKSxdCkZBQ1NfYW5ubyRjZWxsX2lkID0gcGFzdGUoRkFDU19hbm5vJFBsYXRlLHBhc3RlMChGQUNTX2Fubm8kUm93LEZBQ1NfYW5ubyRDb2x1bW4pLHNlcD0iXyIpCnJvd25hbWVzKEZBQ1NfYW5ubykgPSBGQUNTX2Fubm8kY2VsbF9pZApGQUNTX2Fubm8gPSBGQUNTX2Fubm9bLGMoIkRhY2gxR0ZQIiwiQ0QxMWIiLCJDRDE1MCIsIkNEMTI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICJjS2l0IiwiQ0QxMzUiLCJTY2ExIiwiUEkiKV0KIyBGQUNTX2Fubm8gPSBhcHBseShGQUNTX2Fubm8sMixmdW5jdGlvbih4KXtsb2cxMCh4LW1pbihtaW4oeCksMCkrMSl9KQojIEZBQ1NfYW5ubyA9IGFzLmRhdGEuZnJhbWUoRkFDU19hbm5vKQojIEZBQ1NfYW5ubyRDRDEzNVtGQUNTX2Fubm8kQ0QxMzU8Ml0gPSAyCiMgRkFDU19hbm5vJENEMTUwW0ZBQ1NfYW5ubyRDRDE1MDwxLjRdID0gMS40CiMgRkFDU19hbm5vJENEMTFiW0ZBQ1NfYW5ubyRDRDExYjwxLjhdID0gMS44CiMgRkFDU19hbm5vJENEMTI3W0ZBQ1NfYW5ubyRDRDEyNzwxLjVdID0gMS41CiMgRkFDU19hbm5vJERhY2gxR0ZQW0ZBQ1NfYW5ubyREYWNoMUdGUDwxLjldID0gMS45CgpwZGYoIm1hcmtlcl9zY2FsZWRfcGxvdC5wZGYiKQpnZ3Bsb3QoZGF0YT1GQUNTX2Fubm8sYWVzKHg9RGFjaDFHRlAseT1DRDEzNSxjb2w9Q0QxMjcpKSsKICBnZW9tX3BvaW50KGFscGhhPTAuNSkrCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG91cnMgPSByZXYocmFpbmJvdyg1KSkpKwogIHRoZW1lX2J3KCkKZ2dwbG90KGRhdGE9RkFDU19hbm5vLGFlcyh4PUNEMTM1LHk9Q0QxNTAsY29sPUNEMTI3KSkrCiAgZ2VvbV9wb2ludChhbHBoYT0wLjUpKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzID0gcmV2KHJhaW5ib3coNSkpKSsKICB0aGVtZV9idygpCmdncGxvdChkYXRhPUZBQ1NfYW5ubyxhZXMoeD1DRDEyNyx5PWNLaXQpKSsKICBnZW9tX3BvaW50KGFscGhhPTAuNSkrCiAgdGhlbWVfYncoKQpnZ3Bsb3QoZGF0YT1GQUNTX2Fubm8sYWVzKHg9Q0QxMzUseT1DRDEyNykpKwogIGdlb21fcG9pbnQoYWxwaGE9MC41KSsKICB0aGVtZV9idygpCmRldi5vZmYoKQoKY29sRGF0YShzY2VfTEMxODdfcWMpID0gY2JpbmQoY29sRGF0YShzY2VfTEMxODdfcWMpLERhdGFGcmFtZShGQUNTX2Fubm9bY29sbmFtZXMoc2NlX0xDMTg3X3FjKSxdKSkKY29sRGF0YShzY2VfTEMxODhfcWMpID0gY2JpbmQoY29sRGF0YShzY2VfTEMxODhfcWMpLERhdGFGcmFtZShGQUNTX2Fubm9bY29sbmFtZXMoc2NlX0xDMTg4X3FjKSxdKSkKY29sRGF0YShzY2VfTEMxODlfcWMpID0gY2JpbmQoY29sRGF0YShzY2VfTEMxODlfcWMpLERhdGFGcmFtZShGQUNTX2Fubm9bY29sbmFtZXMoc2NlX0xDMTg5X3FjKSxdKSkKY29sRGF0YShzY2VfTEMxOTBfcWMpID0gY2JpbmQoY29sRGF0YShzY2VfTEMxOTBfcWMpLERhdGFGcmFtZShGQUNTX2Fubm9bY29sbmFtZXMoc2NlX0xDMTkwX3FjKSxdKSkKYGBgCgoKYGBge3Isd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFLHJlc3VsdHM9J2hpZGUnfQpzcnRfMTg3IDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBjb3VudHMoc2NlX0xDMTg3X3FjKSxtZXRhLmRhdGE9YXMuZGF0YS5mcmFtZShjb2xEYXRhKHNjZV9MQzE4N19xYykpKQpzcnRfMTg3IDwtIFNDVHJhbnNmb3JtKG9iamVjdCA9IHNydF8xODcsIHZhcmlhYmxlLmZlYXR1cmVzLm49MjAwMCwgdmVyYm9zZSA9IEZBTFNFKQoKc3J0XzE4OCA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gY291bnRzKHNjZV9MQzE4OF9xYyksbWV0YS5kYXRhPWFzLmRhdGEuZnJhbWUoY29sRGF0YShzY2VfTEMxODhfcWMpKSkKc3J0XzE4OCA8LSBTQ1RyYW5zZm9ybShvYmplY3QgPSBzcnRfMTg4LCB2YXJpYWJsZS5mZWF0dXJlcy5uPTIwMDAsdmVyYm9zZSA9IEZBTFNFKQoKc3J0XzE4OSA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gY291bnRzKHNjZV9MQzE4OV9xYyksbWV0YS5kYXRhPWFzLmRhdGEuZnJhbWUoY29sRGF0YShzY2VfTEMxODlfcWMpKSkKc3J0XzE4OSA8LSBTQ1RyYW5zZm9ybShvYmplY3QgPSBzcnRfMTg5LCB2YXJpYWJsZS5mZWF0dXJlcy5uPTIwMDAsdmVyYm9zZSA9IEZBTFNFKQoKc3J0XzE5MCA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gY291bnRzKHNjZV9MQzE5MF9xYyksbWV0YS5kYXRhPWFzLmRhdGEuZnJhbWUoY29sRGF0YShzY2VfTEMxOTBfcWMpKSkKc3J0XzE5MCA8LSBTQ1RyYW5zZm9ybShvYmplY3QgPSBzcnRfMTkwLCB2YXJpYWJsZS5mZWF0dXJlcy5uPTIwMDAsdmVyYm9zZSA9IEZBTFNFKQpgYGAKCmBgYHtyfQpvcHRpb25zKGZ1dHVyZS5nbG9iYWxzLm1heFNpemUgPSAyMTAwICogMTAyNF4yKQppbW11bmUuZmVhdHVyZXMgPC0gU2VsZWN0SW50ZWdyYXRpb25GZWF0dXJlcyhvYmplY3QubGlzdCA9IGxpc3Qoc3J0XzE4Nywgc3J0XzE4OCwgc3J0XzE4OSwgc3J0XzE5MCksIG5mZWF0dXJlcyA9IDIwMDAsIHZlcmJvc2UgPSBGQUxTRSkKaW1tdW5lLmNvbWJpbmVkIDwtIFByZXBTQ1RJbnRlZ3JhdGlvbihvYmplY3QubGlzdCA9IGxpc3Qoc3J0XzE4Nywgc3J0XzE4OCwgc3J0XzE4OSwgc3J0XzE5MCksIGFuY2hvci5mZWF0dXJlcyA9IGltbXVuZS5mZWF0dXJlcywgdmVyYm9zZSA9IEZBTFNFKQoKaW1tdW5lLmFuY2hvcnMgPC0gRmluZEludGVncmF0aW9uQW5jaG9ycyhvYmplY3QubGlzdCA9IGltbXVuZS5jb21iaW5lZCwgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiU0NUIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5jaG9yLmZlYXR1cmVzID0gaW1tdW5lLmZlYXR1cmVzLCB2ZXJib3NlID0gRkFMU0UpCnNydF9jb21iaW5lIDwtIEludGVncmF0ZURhdGEoYW5jaG9yc2V0ID0gaW1tdW5lLmFuY2hvcnMsIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIlNDVCIsIGRpbXMgPSAxOjIwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpCmBgYAoKCmBgYHtyfQpzcnRfY29tYmluZSA9IEZpbmRJbnRlZ3JhdGlvbkFuY2hvcnMob2JqZWN0Lmxpc3QgPSBsaXN0KHNydF8xODcsIHNydF8xODgsIHNydF8xODksIHNydF8xOTApLCBkaW1zID0gMToxNSwgdmVyYm9zZSA9IEZBTFNFKQpzcnRfY29tYmluZSA8LSBJbnRlZ3JhdGVEYXRhKGFuY2hvcnNldCA9IHNydF9jb21iaW5lLCBkaW1zID0gMToxNSwgdmVyYm9zZSA9IEZBTFNFKQpEZWZhdWx0QXNzYXkob2JqZWN0ID0gc3J0X2NvbWJpbmUpIDwtICJpbnRlZ3JhdGVkIgpzcnRfY29tYmluZSA8LSBTY2FsZURhdGEob2JqZWN0ID0gc3J0X2NvbWJpbmUsIHZlcmJvc2UgPSBGQUxTRSkKYGBgCgpgYGB7cix3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UscmVzdWx0cz0naGlkZSd9CnNydF9jb21iaW5lIDwtIFJ1blBDQShvYmplY3QgPSBzcnRfY29tYmluZSwgdmVyYm9zZSA9IEZBTFNFKQpzcnRfY29tYmluZSA8LSBSdW5VTUFQKG9iamVjdCA9IHNydF9jb21iaW5lLCBkaW1zID0gMToxNSwgdmVyYm9zZSA9IEZBTFNFKQpzcnRfY29tYmluZSA8LSBGaW5kTmVpZ2hib3JzKG9iamVjdCA9IHNydF9jb21iaW5lLCBkaW1zID0gMToxNSwgdmVyYm9zZSA9IEZBTFNFKQpzcnRfY29tYmluZSA8LSBGaW5kQ2x1c3RlcnMob2JqZWN0ID0gc3J0X2NvbWJpbmUsIHZlcmJvc2UgPSBGQUxTRSkKIyBWaXN1YWxpemF0aW9uCnAxIDwtIERpbVBsb3Qoc3J0X2NvbWJpbmUsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiYmF0Y2giKQpwMiA8LSBEaW1QbG90KHNydF9jb21iaW5lLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVFJVRSkKYGBgCgpgYGB7cixmaWcud2lkdGg9MTAsZmlnLmhlaWdodD01fQpwbG90X2dyaWQocDEsIHAyKQpgYGAKCgpgYGB7cixmaWcuaGVpZ2h0PTgsZmlnLndpZHRoPTEwfQpzcnRfY29tYmluZSA9IHNydF9jb21iaW5lWywhaXMubmEoc3J0X2NvbWJpbmUkRGFjaDFHRlApXQpGZWF0dXJlUGxvdChvYmplY3QgPSBzcnRfY29tYmluZSwgZmVhdHVyZXMgPWMoIkRhY2gxR0ZQIiwiQ0QxMWIiLCJDRDE1MCIsIkNEMTI3IiwiY0tpdCIsIkNEMTM1IiwiU2NhMSIsIlBJIiksIHB0LnNpemUgPSAwLjUpCmBgYAoKCmBgYHtyLGZpZy5oZWlnaHQ9NyxmaWcud2lkdGg9N30KVmxuUGxvdChvYmplY3QgPSBzcnRfY29tYmluZSwgZmVhdHVyZXM9IGMoIkdhdGEyIiwiRG50dCIsIkN0c2ciLCJEYWNoMSIsIkdhdGExIiksbmNvbD0yLHB0LnNpemU9MC4xKQpgYGAKCgpgYGB7cn0Kc3J0X2NvbWJpbmUubWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhzcnRfY29tYmluZSwgb25seS5wb3MgPSBUUlVFLHZlcmJvc2U9RkFMU0UpCmBgYAoKYGBge3IsZmlnLmhlaWdodD0xMixmaWcud2lkdGg9MTB9CnRvcDEwIDwtIHNydF9jb21iaW5lLm1hcmtlcnMgJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSB0b3BfbihuID0gMTAsIHd0ID0gYXZnX2xvZ0ZDKQpEb0hlYXRtYXAoc3J0X2NvbWJpbmUsIGZlYXR1cmVzID0gdG9wMTAkZ2VuZSkKYGBgCgpgYGB7cixmaWcuaGVpZ2h0PTcsZmlnLndpZHRoPTd9ClZsblBsb3Qob2JqZWN0ID0gc3J0X2NvbWJpbmVbLCFpcy5uYShzcnRfY29tYmluZSRDRDEzNSldLCBmZWF0dXJlcz0gYygiRGFjaDFHRlAiLCJDRDE1MCIsIkNEMTM1IiwiQ0QxMjciLCJjS2l0IiksbmNvbD0yLHB0LnNpemU9MC4xKQpgYGAKCgpgYGB7cn0KQ0QxMjdfdGhyZXNob2xkID0gOTAwCmdncGxvdChkYXRhPU5VTEwsYWVzKHg9c3J0X2NvbWJpbmVAbWV0YS5kYXRhJENEMTM1LHk9c3J0X2NvbWJpbmVAbWV0YS5kYXRhJENEMTI3LGNvbD1zcnRfY29tYmluZUBhY3RpdmUuaWRlbnQpKSsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBDRDEyN190aHJlc2hvbGQsbGluZXR5cGU9ImRvdHRlZCIpKwogIGdlb21fcG9pbnQoYWxwaGE9MC41LHNpemU9MikrCiAgbGFicyh4PSJDRDEzNSIseT0iQ0QxMjciKSsKICB0aGVtZV9idygpCmdncGxvdChkYXRhPU5VTEwsYWVzKHg9c3J0X2NvbWJpbmVAbWV0YS5kYXRhJGNLaXQseT1zcnRfY29tYmluZUBtZXRhLmRhdGEkQ0QxMjcsY29sPXNydF9jb21iaW5lQGFjdGl2ZS5pZGVudCkpKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IENEMTI3X3RocmVzaG9sZCxsaW5ldHlwZT0iZG90dGVkIikrCiAgZ2VvbV9wb2ludChhbHBoYT0wLjUsc2l6ZT0yKSsKICBsYWJzKHg9ImNLaXQiLHk9IkNEMTI3IikrCiAgdGhlbWVfYncoKQpgYGAKCiMgZmlsdGVyIGNlbGxzCgoqIHJlbW92ZSBDRDEyNyBoaWdoCiogcmVtb3ZlIGNsdXN0ZXIgNyBhbmQgOAoKYGBge3J9CiNzcnRfY29tYmluZV9maWx0ZXIgPC0gU3Vic2V0RGF0YShvYmplY3QgPSBzcnRfY29tYmluZSxpZGVudC5yZW1vdmUgPSBjKDcsOCkpCnNydF9jb21iaW5lX2ZpbHRlciA8LSBTdWJzZXREYXRhKG9iamVjdCA9IHNydF9jb21iaW5lX2ZpbHRlciwgc3Vic2V0Lm5hbWU9IkNEMTI3IixoaWdoLnRocmVzaG9sZD1DRDEyN190aHJlc2hvbGQpCmBgYAoKCmBgYHtyLHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxyZXN1bHRzPSdoaWRlJ30Kc3J0X2NvbWJpbmVfZmlsdGVyIDwtIFJ1blBDQShvYmplY3QgPSBzcnRfY29tYmluZV9maWx0ZXIsIHZlcmJvc2UgPSBGQUxTRSkKc3J0X2NvbWJpbmVfZmlsdGVyIDwtIFJ1blVNQVAob2JqZWN0ID0gc3J0X2NvbWJpbmVfZmlsdGVyLCBkaW1zID0gMToxNSwgdmVyYm9zZSA9IEZBTFNFKQpzcnRfY29tYmluZV9maWx0ZXIgPC0gRmluZE5laWdoYm9ycyhvYmplY3QgPSBzcnRfY29tYmluZV9maWx0ZXIsIGRpbXMgPSAxOjE1LCB2ZXJib3NlID0gRkFMU0UpCnNydF9jb21iaW5lX2ZpbHRlciA8LSBGaW5kQ2x1c3RlcnMob2JqZWN0ID0gc3J0X2NvbWJpbmVfZmlsdGVyLCB2ZXJib3NlID0gRkFMU0UpCiMgVmlzdWFsaXphdGlvbgpwMSA8LSBEaW1QbG90KHNydF9jb21iaW5lX2ZpbHRlciwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJiYXRjaCIpCnAyIDwtIERpbVBsb3Qoc3J0X2NvbWJpbmVfZmlsdGVyLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVFJVRSkKYGBgCgpgYGB7cixmaWcud2lkdGg9MTAsZmlnLmhlaWdodD01fQpwbG90X2dyaWQocDEsIHAyKQpgYGAKCiMgaGVhdG1hcCBhZnRlciBmaWx0ZXJpbmcKCmBgYHtyLGZpZy5oZWlnaHQ9MTAsZmlnLndpZHRoPTE3fQpzcnRfY29tYmluZV9maWx0ZXIubWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhzcnRfY29tYmluZV9maWx0ZXIsIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMSxvbmx5LnBvcyA9IFRSVUUsdmVyYm9zZT1GQUxTRSkKdG9wMTAgPC0gc3J0X2NvbWJpbmVfZmlsdGVyLm1hcmtlcnMgJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSB0b3BfbihuID0gMTAsIHd0ID0gYXZnX2xvZ0ZDKQpEb0hlYXRtYXAoc3J0X2NvbWJpbmVfZmlsdGVyLCBmZWF0dXJlcyA9IHRvcDEwJGdlbmUpCmBgYAoKCmBgYHtyLGZpZy5oZWlnaHQ9NyxmaWcud2lkdGg9N30KVmxuUGxvdChvYmplY3QgPSBzcnRfY29tYmluZV9maWx0ZXIsIGZlYXR1cmVzPSBjKCJEYWNoMUdGUCIsIkNEMTUwIiwiQ0QxMzUiLCJDRDEyNyIsImNLaXQiKSxuY29sPTIscHQuc2l6ZT0wLjEpCmBgYAoKYGBge3IsZmlnLmhlaWdodD04LGZpZy53aWR0aD0xMH0KRmVhdHVyZVBsb3Qob2JqZWN0ID0gc3J0X2NvbWJpbmVfZmlsdGVyLCBmZWF0dXJlcyA9YygiRGFjaDFHRlAiLCJDRDExYiIsIkNEMTUwIiwiQ0QxMjciLCJjS2l0IiwiQ0QxMzUiLCJTY2ExIiwiUEkiKSwgcHQuc2l6ZSA9IDAuNSkKYGBgCgpgYGB7cixmaWcud2lkdGg9OSxmaWcuaGVpZ2h0PTh9CnAxPWdncGxvdChkYXRhPU5VTEwsYWVzKHg9c3J0X2NvbWJpbmVfZmlsdGVyQG1ldGEuZGF0YSRDRDEzNSx5PXNydF9jb21iaW5lX2ZpbHRlckBtZXRhLmRhdGEkQ0QxNTAsY29sPXNydF9jb21iaW5lX2ZpbHRlckBhY3RpdmUuaWRlbnQpKSsKICBnZW9tX3BvaW50KGFscGhhPTAuNSxzaXplPTIpKwogIGxhYnMoeD0iQ0QxMzUiLHk9IkNEMTUwIixjb2w9ImNsdXN0ZXIiKSsKICB0aGVtZV9idygpCnAyPWdncGxvdChkYXRhPU5VTEwsYWVzKHg9c3J0X2NvbWJpbmVfZmlsdGVyQG1ldGEuZGF0YSREYWNoMUdGUCx5PXNydF9jb21iaW5lX2ZpbHRlckBtZXRhLmRhdGEkQ0QxMzUsY29sPXNydF9jb21iaW5lX2ZpbHRlckBhY3RpdmUuaWRlbnQpKSsKICBnZW9tX3BvaW50KGFscGhhPTAuNSxzaXplPTIpKwogIGxhYnMoeD0iRGFjaDFHRlAiLHk9IkNEMTM1Iixjb2w9ImNsdXN0ZXIiKSsKICB0aGVtZV9idygpCgpwMz1nZ3Bsb3QoZGF0YT1OVUxMLGFlcyh4PXNydF9jb21iaW5lX2ZpbHRlckBtZXRhLmRhdGEkY0tpdCx5PXNydF9jb21iaW5lX2ZpbHRlckBtZXRhLmRhdGEkU2NhMSxjb2w9c3J0X2NvbWJpbmVfZmlsdGVyQGFjdGl2ZS5pZGVudCkpKwogIGdlb21fcG9pbnQoYWxwaGE9MC41LHNpemU9MikrCiAgbGFicyh4PSJjS2l0Iix5PSJTY2ExIixjb2w9ImNsdXN0ZXIiKSsKICB0aGVtZV9idygpCgpwND1nZ3Bsb3QoZGF0YT1OVUxMLGFlcyh4PXNydF9jb21iaW5lX2ZpbHRlckBtZXRhLmRhdGEkQ0QxMzUseT1zcnRfY29tYmluZV9maWx0ZXJAbWV0YS5kYXRhJENEMTI3LGNvbD1zcnRfY29tYmluZV9maWx0ZXJAYWN0aXZlLmlkZW50KSkrCiAgZ2VvbV9wb2ludChhbHBoYT0wLjUsc2l6ZT0yKSsKICBsYWJzKHg9IkNEMTM1Iix5PSJDRDEyNyIsY29sPSJjbHVzdGVyIikrCiAgdGhlbWVfYncoKQoKcGxvdF9ncmlkKHAxLCBwMiwgcDMsIHA0LG5jb2w9MikKYGBgCgojIGZpbmQgcG90ZW50aWFsIHN1cmZhY2UgcHJvdGVpbgoKYGBge3J9Cm1vdXNlX3N1cmZhY2VfcHJvdGVpbiA8LSByZWFkX2Nzdigifi9Ecm9wYm94L3Jlc2VhcmNoL3Npc19zZXEvbW91c2Vfc3VyZmFjZV9wcm90ZWluLmNzdiIpCmBgYAoKYGBge3J9CnN1cmZhY2VfcHJvX25hbWUgPSBtb3VzZV9zdXJmYWNlX3Byb3RlaW4kYEVOVFJFWiBnZW5lIHN5bWJvbGAKc3VyZmFjZV9wcm9fbmFtZSA9IHN1cmZhY2VfcHJvX25hbWVbc3VyZmFjZV9wcm9fbmFtZSAlaW4lIHJvd25hbWVzKHNydF9jb21iaW5lX2ZpbHRlcildCmxlbmd0aChzdXJmYWNlX3Byb19uYW1lKQpgYGAKCmBgYHtyfQpjbHVzdGVyLnN1cmZhY2VfbWFya2VycyA9IHNydF9jb21iaW5lX2ZpbHRlci5tYXJrZXJzCmNsdXN0ZXIuc3VyZmFjZV9tYXJrZXJzJGlzX3N1cmZhY2VfcHJvPUZBTFNFCmNsdXN0ZXIuc3VyZmFjZV9tYXJrZXJzJGlzX3N1cmZhY2VfcHJvW3Jvd25hbWVzKGNsdXN0ZXIuc3VyZmFjZV9tYXJrZXJzKSAlaW4lIHN1cmZhY2VfcHJvX25hbWVdID0gVFJVRQpjbHVzdGVyLnN1cmZhY2VfbWFya2VycyA9IGNsdXN0ZXIuc3VyZmFjZV9tYXJrZXJzW2NsdXN0ZXIuc3VyZmFjZV9tYXJrZXJzJGlzX3N1cmZhY2VfcHJvLF0KY2x1c3Rlci5zdXJmYWNlX21hcmtlcnMKYGBgCgojIGhlYXRtYXAgb2YgYWxsIHN1cmZhY2UgcHJvdGVpbnMKCmBgYHtyLGZpZy5oZWlnaHQ9MTAsZmlnLndpZHRoPTE3fQpEb0hlYXRtYXAoc3J0X2NvbWJpbmVfZmlsdGVyLCBmZWF0dXJlcyA9IHJvd25hbWVzKGNsdXN0ZXIuc3VyZmFjZV9tYXJrZXJzKSkKYGBgCgojIHN1cmZhY2UgcHJvdGlucyB0aGF0IHJhbGF0ZWQgdG8gY2x1c3RlciAyCgpgYGB7cn0KY2x1c3Rlci5zdXJmYWNlX21hcmtlcnNfMiA9IGNsdXN0ZXIuc3VyZmFjZV9tYXJrZXJzW2NsdXN0ZXIuc3VyZmFjZV9tYXJrZXJzJGNsdXN0ZXI9PTIsXQpjbHVzdGVyLnN1cmZhY2VfbWFya2Vyc18yCmBgYAoKYGBge3J9CmltbV9kYXRhID0gcmVhZC5jc3YoIi9Vc2Vycy90aWFuLmwvRHJvcGJveC9yZXNlYXJjaC9zaXNfc2VxL2ltbWdlbi9WMV9JbW1HZW5uLU9mZmljaWFsLU9jdC0yMDEyLmNzdiIsIHNraXA9MiwgY2hlY2submFtZXM9RkFMU0UsIGFzLmlzPVRSVUUpCmNlbGwuZmFjID0gZ3N1YigiIy4uLi4iLCAiIiwgY29sbmFtZXMoaW1tX2RhdGEpKQpjZWxsLmZhYyA9IGdzdWIoIiMuLi4iLCAiIiwgY2VsbC5mYWMpCmNlbGwuZmFjID0gZ3N1YigiIy4iLCAiIiwgY2VsbC5mYWMpCmNlbGwuZmFjID0gIGdzdWIoIlxcLiIsICJfIiwgY2VsbC5mYWMpCmNlbGwuZmFjID0gIGdzdWIoIlxcLSIsICJuZWciLCBjZWxsLmZhYykKY2VsbC5mYWMgPSAgZ3N1YigiXFwrIiwgInBvcyIsIGNlbGwuZmFjKQpjZWxsLmZhYyA9ICBnc3ViKCJcXC8iLCAiZGl2IiwgY2VsbC5mYWMpCmNvbG5hbWVzKGltbV9kYXRhKSA9IGNlbGwuZmFjCgppbW1fZGF0YV9zdXJmYWNlID0gaW1tX2RhdGFbaW1tX2RhdGEkYEdlbmUgU3ltYm9sYCAlaW4lIGNsdXN0ZXIuc3VyZmFjZV9tYXJrZXJzXzIkZ2VuZSxdCmltbV9kYXRhX3N1cmZhY2UgPSBpbW1fZGF0YV9zdXJmYWNlWyFkdXBsaWNhdGVkKGltbV9kYXRhX3N1cmZhY2UkYEdlbmUgU3ltYm9sYCksXQpyb3duYW1lcyhpbW1fZGF0YV9zdXJmYWNlKSA9IGltbV9kYXRhX3N1cmZhY2UkYEdlbmUgU3ltYm9sYAppbW1fZGF0YV9zdXJmYWNlPWltbV9kYXRhX3N1cmZhY2VbLC1jKDEsMildCmltbV9kYXRhX3N1cmZhY2UgPSBhcy5tYXRyaXgoaW1tX2RhdGFfc3VyZmFjZSkKCnBkZigiY2x1c3RlcjJfc3VyZmFjZV9wcm90ZWluLmltbWdlbi5wZGYiLHdpZHRoPTE1LGhlaWdodCA9IDgwKQpwaGVhdG1hcDo6cGhlYXRtYXAodChsb2cyKGltbV9kYXRhX3N1cmZhY2UrMSkpLHNjYWxlID0gImNvbHVtbiIsZm9udHNpemVfY29sPTMsZm9udHNpemVfcm93PTEwKQpkZXYub2ZmKCkKYGBgCgpgYGB7cn0KY2x1Mi5tYXJrZXJzID0gRmluZE1hcmtlcnMoc3J0X2NvbWJpbmVfZmlsdGVyLGlkZW50LjE9Mix2ZXJib3NlID0gRkFMU0UpCmNsdTIubWFya2VycwpgYGAKCmBgYHtyLGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTZ9CmNsdTIubWFya2VycyRnZW5lX25hbWUgPSAiIgpjbHUyLm1hcmtlcnMkZ2VuZV9uYW1lW2NsdTIubWFya2VycyRwX3ZhbF9hZGo8MC4wMDAxICYgY2x1Mi5tYXJrZXJzJGF2Z19sb2dGQzwwXT0gcm93bmFtZXMoY2x1Mi5tYXJrZXJzKVtjbHUyLm1hcmtlcnMkcF92YWxfYWRqPDAuMDAwMSAmIGNsdTIubWFya2VycyRhdmdfbG9nRkM8MF0KY2x1Mi5tYXJrZXJzJGdlbmVfbmFtZVtjbHUyLm1hcmtlcnMkcF92YWxfYWRqPDFlLTcgJiBjbHUyLm1hcmtlcnMkYXZnX2xvZ0ZDPjBdPSByb3duYW1lcyhjbHUyLm1hcmtlcnMpW2NsdTIubWFya2VycyRwX3ZhbF9hZGo8MWUtNyAmIGNsdTIubWFya2VycyRhdmdfbG9nRkM+MF0KCmdncGxvdChkYXRhPWNsdTIubWFya2VycyxhZXMoeD0tbG9nMTAocF92YWxfYWRqKSx5PWF2Z19sb2dGQyxsYWJlbD1nZW5lX25hbWUpKSsKICBnZW9tX3BvaW50KCkrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMixsaW5ldHlwZT0iZG90dGVkIikrCiAgZ2VvbV90ZXh0X3JlcGVsKCkrCiAgdGhlbWVfYncoKQpgYGAKCgoKCmBgYHtyfQpzYXZlLmltYWdlKCJ+L0Ryb3Bib3gvcmVzZWFyY2gvRGFjaDFfcGFwZXIvTk4xMjYvTk4xMjZfU2V1cmF0X2FuYWx5c2lzLlJEYXRhIikKYGBgCgo=